/* Susan(tm) Java Version
   by Thomas Lochmatter, Brig-Glis, Switzerland
   and Patrick Bizeau, Brig-Glis, Switzerland    */

package laWa;
import java.util.*;
import java.io.*;

// The Game itself. Manages the field an all operation within it.
// Also contains the computer strategies.
class SusanGame {

  // ######################### INSTANCE VARIABLES

  // The Susan Field
  public int[] field = {0,0,0,0,0,0,0,0,0,0,0,
                        0,4,4,4,4,4,0,0,0,0,0,
                        0,4,4,4,4,4,4,0,0,0,0,
                        0,4,4,4,4,4,4,4,0,0,0,
                        0,4,4,4,4,4,4,4,4,0,0,
                        0,4,4,4,4,4,4,4,4,4,0,
                        0,0,4,4,4,4,4,4,4,4,0,
                        0,0,0,4,4,4,4,4,4,4,0,
                        0,0,0,0,4,4,4,4,4,4,0,
                        0,0,0,0,0,4,4,4,4,4,0,
                        0,0,0,0,0,0,0,0,0,0,0,};

  // The value of field is a bitmask:
  public static final int fieldplayer=1;       // 0 for player 1, 1 for player 2
  public static final int fieldoccupied=2;     // occupied by a player's stone
  public static final int fieldexists=4;       // field exists
  public static final int fielddontcare=8;     // don't take care of this stone in db entry
  public static final int fieldmarkdrag=16;    // marked for a valid drag
  public static final int fieldmarklost=32;    // the game is lost because of this stone
  public static final int fieldmarkfort=64;    // field is protected by an arc
  public static final int fieldmarklooser=128; // this own stone is enclosed by 5 others
  public static final int fieldmarkvictim=256; // this opponent's stone is enclosed by 5 others
  public static final int fieldmarkcalc=512;   // this field could be interesting for move precalculation
  public static final int fieldtemp=1024;      // temporarily used bit
  public static final int fieldtemp2=2048;     // temporarily used bit

  // This lets us easily use bitwise operations to check and change the status
  // of fields:
  public static final int fieldexoccupied=6;       // mask: occupied, exists
  public static final int fieldexoccupiedno=4;     // not occupied, but exists
  public static final int fieldexoccupiedyes=6;    // occupied and exists
  public static final int fieldexoccpl=7;          // mask: player, occupied, exists

  // The magic field index
  // This field is used as noField-value
  public static final int magicfield=19;

  // The stones around a field
  static final int around[]={-12,-11,-1,1,11,12};

  // Who's turn is it?
  public int playturn;

  // Status of the game
  int status=0;
  public static final int statusstopped=0;
  public static final int statusplaying=1;
  public static final int statusfinished=2;

  // The last move the computer had calculated
  public int computerfrom;
  public int computerto;
  public String computermessage;
  //int[] cedge={57,24,27,63,96,93,57,24};

  // Variables used by the computer strategy methods
  int[] cf;
  int[] cfmark;
  int cfstonescount;
  PrintStream clog;
  int[] cmvfrom;
  int[] cmvto;
  int[] cmvparent;
  int[] cmvpoints;
  int[] cmvcalculate;
  int[] cmvdeepstart;
  int cmvdeepness;
  static final int cmvploose=-1000000000;
  static final int cmvpwin=1000000000;
  static final int cmvploosebound=-500000000;
  static final int cmvpwinbound=500000000;
  int cmvnw;
  boolean cmvcanwin;
  int cmvdragcount;
  int cmvstack=100000;
  static final int cmvminimalstack=300;
  int[] tree=new int[100];
  int treed, treep;

  // The associated database
  private SusanDB sdb;

  // The stone already chosen, this is used to perform drags
  // if this is equal to the magicfield, no stone is chosen
  public int dragfrom=magicfield;

  // Count the drags, because of the rule of the three drags
  int dragcount=0;

  // This is for the game rollback
  java.util.List history;
  public int historypos;

  // The handler of SusanGame events
  SusanGameListener eventhandler=null;

  // ######################### CONSTRUCTOR
  SusanGame(int beginplayturn, SusanGameListener sgl, SusanDB sdb) {
    status=statusplaying;
    playturn=beginplayturn;
    history=new ArrayList();
    this.sdb=sdb;

    if (sgl==null) {
      eventhandler = new SusanGameMultiListener();
    } else {
      eventhandler = sgl;
    }

    cf=new int[121];
    cfmark=new int[121];
  }

  // ######################### BASIC GAME FUNCTIONALITY
  // These methods are needed to play Susan.

  // Performs a drag if possible
  public boolean Drag(int from, int to) {
    if (!canDrag(from,to)) {return false;}
    field[to]=field[from];
    field[from] ^= fieldoccupied;
    HistoryAdd(new SusanGameHistoryElement(from,to,dragcount));
    dragcount++;
    eventhandler.SusanGameDragPerformed(playturn,from,to);
    FinishAction();
    return true;
  }

  // Performs a drag with the already set dragfrom variable
  public boolean Drag(int to) {
    return Drag(dragfrom,to);
  }

  // Checks if a drag is possible
  public boolean canDrag(int from, int to) {
    if (status!=statusplaying) {return false;}
    if ((field[from] & fieldexoccupied)!=fieldexoccupiedyes) {return false;}
    if ((field[from] & fieldplayer)!=playturn) {return false;}
    if ((field[to] & fieldexoccupied)!=fieldexoccupiedno) {return false;}
    int diff=from-to;
    for(int i=0; i<6; i++) {
      if (around[i]==diff) {return true;}
    }
    return false;
  }

  // Checks if a drag with this stone would be possible
  public boolean canDrag(int from) {
    if (status!=statusplaying) {return false;}
    if ((field[from] & fieldexoccupied)!=fieldexoccupiedyes) {return false;}
    if ((field[from] & fieldplayer)!=playturn) {return false;}
    for(int i=0; i<6; i++) {
      if (((field[from+around[i]] & fieldoccupied)==0) && ((field[from+around[i]] & fieldoccupied)==0)) {return true;}
    }
    return false;
  }

  // Marks the possible drags around a field or deletes the marks
  public boolean markDrag(int from) {
    boolean changed=false;
    for(int i=0; i<6; i++){
      if ((field[dragfrom+around[i]] & fieldmarkdrag)!=0) {field[dragfrom+around[i]] ^= fieldmarkdrag; changed=true;}
    }
    if (dragfrom==from) {dragfrom=magicfield; return changed;}
    dragfrom=from;
    for(int i=0; i<6; i++){
      if ((field[dragfrom+around[i]] & fieldexoccupied)==fieldexoccupiedno) {field[dragfrom+around[i]] |= fieldmarkdrag; changed=true;}
    }
    return changed;
  }

  // Performs a placement if possible
  public boolean Place(int to) {
    if (!canPlace(to)) {return false;}
    field[to] |= fieldoccupied | fieldplayer;
    field[to] ^= (1-playturn);
    HistoryAdd(new SusanGameHistoryElement(to,dragcount));
    dragcount=0;
    eventhandler.SusanGamePlacePerformed(playturn,to);
    FinishAction();
    return true;
  }

  // Checks if a placement is possible
  public boolean canPlace(int to) {
    if (status!=statusplaying) {return false;}
    return ((field[to] & fieldexoccupied)==fieldexoccupiedno);
  }

  // Finishes a drag or a placement
  private void FinishAction () {
    if (dragcount==6) {
      status=statusfinished;
      eventhandler.SusanGameFinished(-1);
    } else {
      int winner=hasWinner(playturn);
      markWinner(winner);
      playturn=1-playturn;

      if (winner==-1) {
        eventhandler.SusanGamePrepareNextTurn(playturn);
      } else {
        status=statusfinished;
        eventhandler.SusanGameFinished(winner);
      }
    }
    eventhandler.SusanGameHistoryChanged();
  }

  // Check if someone has won the game
  private int hasWinner(int lastplayer) {
    int winner=-1;
    for (int i=0; i<121; i++) {
      if ((field[i] & fieldexoccupied)==fieldexoccupiedyes) {
        int n=0;
        for (; n<6; n++) {
          if ((field[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {
          if ((field[i] & fieldplayer)==lastplayer) {
            return 1-lastplayer;
          } else {
            winner=lastplayer;
          }
        }
      }
    }
    return winner;
  }

  // Marks the loosing stones with the fieldmarklost flag
  private void markWinner(int winner) {
    for (int i=0; i<121; i++) {
      field[i] |= fieldmarklost;
      field[i] ^= fieldmarklost;
      if ((field[i] & fieldexoccupied)==fieldexoccupiedyes) {
        int n=0;
        for (; n<6; n++) {
          if ((field[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {
          if ((field[i] & fieldplayer)!=winner) {field[i] |= fieldmarklost;}
        }
      }
    }
  }

  // ######################### COORDINATE TRANSFORM
  // SusanGame works internally with special coordinates to simplify and
  // minimize calculation. These methods for transformation between the
  // different systems.

  // Transforms the interal coordinates into a1-coordinates
  public static String toString(int coord) {
    char[] cc=new char[2];
    cc[0]=(char)(coord / 11);
    cc[1]=(char)(coord % 11);
    if (cc[0]>5) {cc[1]-=(cc[0]-5);}
    cc[0]+=96;
    cc[1]+=48;
    return new String(cc);
  }

  // Gets the row of a field
  public static char getRow(int coord) {
    char c=(char)(coord / 11 + 96);
    return c;
  }

  // Gets the column of a field
  public static int getColumn(int coord) {
    int row=coord / 11;
    int col=coord % 11;
    if (row>5) {col-=(row-5);}
    return col;
  }

  // Transforms the a1-coordinates into interal coordinates
  // Returns magicfield if the a1-coordinates are invalid
  public static int getPosition(String t) {
    try {
    	int row=t.charAt(0)-96;
      int col=t.charAt(1)-48;
      if ((col<1) || (col>9)) {return magicfield;}
      if ((row<1) || (row>9)) {return magicfield;}
      if (row>5) {col+=(row-5);}
      return row*11+col;
    } catch (Exception e) {
      return magicfield;
    }
  }

  // Transforms the row/col into interal coordinates
  // Returns magicfield if the coordinates are invalid
  public static int getPosition(char rw, int col) {
    int row=rw-96;
    if ((col<1) || (col>9)) {return magicfield;}
    if ((row<1) || (row>9)) {return magicfield;}
    if (row>5) {col+=(row-5);}
    return row*11+col;
  }

  // ######################### GAME ANALYSE
  // The following methods are looking though the field on order to find and
  // mark special stones.

  // Find stones which are unbeatable at the current moment
  public boolean calcFortress (int player) {
    int fieldoc=fieldplayer | fieldoccupied;
    int fieldexoccplme=fieldexoccupiedyes | player;
    //int fieldpl=fieldoccupied | player;
    int fieldexoccplother=fieldexoccupiedyes | (1-player);
    for (int i=0; i<121; i++) {
      if ((field[i] & fieldexists)!=0) {
        int n;
        field[i] |= fieldtemp;
        for (n=0; n<6; n++) {
          field[i+around[n]] |= fieldtemp;
        }
        for (n=0; n<6; n++) {
          int m=i+around[n];
          if (((field[m] & fieldexoccupied)==fieldexoccupiedno) || ((field[m] & fieldexoccpl)==fieldexoccplother)) {
            int w=0;
            for (; w<6; w++) {
              if ((field[m+around[w]] & fieldtemp)!=0) {
              } else if ((field[m+around[w]] & fieldexists)==0) {
              } else if ((field[m+around[w]] & fieldexoccpl)==fieldexoccplme) {
              //} else if ((field[m+around[w]] & fieldoc)==fieldpl) {
              } else {
                break;
              }
            }
            if (w==6) {break;}
          }
        }
        field[i]|=fieldmarkfort;
        if (n==6) {field[i]^=fieldmarkfort;}
        field[i] ^= fieldtemp;
        for (n=0; n<6; n++) {
          field[i+around[n]] ^= fieldtemp;
        }
      } else {
        field[i]|=fieldmarkfort;
        field[i]^=fieldmarkfort;
      }
    }
    return true;
  }

  // Delete all fortress marks
  public boolean hideFortress () {
    for (int i=0; i<121; i++) {
      field[i]|=fieldmarkfort;
      field[i]^=fieldmarkfort;
    }
    return true;
  }

  // Find own stones which are enclosed by 5 stones
  public boolean calcLoosers (int player) {
    int fieldoc=fieldplayer | fieldoccupied;
    int fieldpl=fieldoccupied | player;
    for (int i=0; i<121; i++) {
      if ((field[i] & fieldoc)==fieldpl) {
        int n;
        int free=0;
        for (n=0; n<6; n++) {
          int m=i+around[n];
          if ((field[m] & fieldexoccupied)==fieldexoccupiedno) {
            free++;
            if (free==2) {break;}
          }
        }
        field[i]|=fieldmarklooser;
        if (free!=1) {field[i]^=fieldmarklooser;}
      } else {
        field[i]|=fieldmarklooser;
        field[i]^=fieldmarklooser;
      }
    }
    return true;
  }

  // Delete all looser marks
  public boolean hideLoosers () {
    for (int i=0; i<121; i++) {
      field[i]|=fieldmarklooser;
      field[i]^=fieldmarklooser;
    }
    return true;
  }

  // Find opponent's stones which are enclosed by 5 stones
  public boolean calcVictims (int player) {
    int fieldoc=fieldplayer | fieldoccupied;
    int fieldpl=fieldoccupied | (1-player);
    for (int i=0; i<121; i++) {
      if ((field[i] & fieldoc)==fieldpl) {
        int n;
        int free=0;
        for (n=0; n<6; n++) {
          int m=i+around[n];
          if ((field[m] & fieldexoccupied)==fieldexoccupiedno) {
            free++;
            if (free==2) {break;}
          }
        }
        field[i]|=fieldmarkvictim;
        if (free!=1) {field[i]^=fieldmarkvictim;}
      } else {
        field[i]|=fieldmarkvictim;
        field[i]^=fieldmarkvictim;
      }
    }
    return true;
  }

  // Delete all looser marks
  public boolean hideVictims () {
    for (int i=0; i<121; i++) {
      field[i]|=fieldmarkvictim;
      field[i]^=fieldmarkvictim;
    }
    return true;
  }

  // ######################### HISTORY MANAGEMENT
  // These methods manage the game history.

  // Adds a drag or a placed stone to the history
  private void HistoryAdd(SusanGameHistoryElement sghe) {
    for (int i=history.size()-1; i>=historypos; i--) {
      history.remove(i);
    }
    history.add(sghe);
    historypos++;
  }

  // Checks if a jump in the past is possible
  public boolean canHistoryBack() {
    return (historypos>0);
  }

  // Jumps the given amount of steps back, if possible.
  public boolean HistoryBack(int count) {
    if (! canHistoryBack()) {return false;}

    SusanGameHistoryElement sghe=null;
    if (count>historypos) {count=historypos;}
    for (int i=0; i<count; i++) {
      historypos--;
      sghe=(SusanGameHistoryElement)history.get(historypos);
      if (sghe.from==magicfield) {
        field[sghe.to] ^= fieldoccupied;
      } else {
        field[sghe.from]=field[sghe.to];
        field[sghe.to] ^= fieldoccupied;
      }
    }
    dragcount=sghe.olddragcount;
    if ((count % 2)==1) {playturn=1-playturn;}
    HistoryFinishAction();
    return true;
  }

  // Checks whether a jump in the bright future of orange is possible
  public boolean canHistoryNext() {
    return (historypos<history.size());
  }

  // Jumps the given amount of steps forward in history
  public boolean HistoryNext(int count) {
    if (! canHistoryNext()) {return false;}

    SusanGameHistoryElement sghe=null;
    if (count>history.size()-historypos) {count=history.size()-historypos;}
    for (int i=0; i<count; i++) {
      sghe=(SusanGameHistoryElement)history.get(historypos);
      playturn=1-playturn;
      if (sghe.from==magicfield) {
        field[sghe.to] |= fieldplayer | fieldoccupied;
        field[sghe.to] ^= playturn;
        dragcount=0;
      } else {
        field[sghe.to]=field[sghe.from];
        field[sghe.from] ^= fieldoccupied;
        dragcount=sghe.olddragcount+1;
      }
      historypos++;
    }
    HistoryFinishAction();
    return true;
  }

  // Does the needed work after going up/down in history. Especially some
  // events need to be called.
  private void HistoryFinishAction() {
    if (dragcount==6) {
      status=statusfinished;
      eventhandler.SusanGameFinished(-1);
    } else {
      int winner=hasWinner(1-playturn);
      markWinner(winner);

      if (winner==-1) {
        status=statusplaying;
        eventhandler.SusanGamePrepareNextTurn(playturn);
      } else {
        status=statusfinished;
        eventhandler.SusanGameFinished(winner);
      }
    }
    eventhandler.SusanGameHistoryPositionChanged();
  }

  // ######################### COMPUTER
  // The brain of the game.

  // This method calculates a move. It always returns true. The boolean value
  // false is reserved for testing and debugging the algorithm and means, that
  // the computer doesn't know what to do.
  // The move is stored in the computerfrom and computerto variables after
  // returning from this method. If the computerfrom value is the magicfield,
  // it means that the computer wanted to place a stone on the field specified
  // by computerto. Otherwise, the computer decided to move the stone on the
  // field given by computerfrom to the field stored in computerto.
  // Variables used in the recursive algorithm are declared as instance
  // variables instead of passing them in the argument, because this seems to
  // be faster an easier.
  public boolean Computer () {
    computerfrom=magicfield;
    computerto=magicfield;
    int i;

    ComputerLogStart(true);
    ComputerLogStatus("Preparing ...");

    // Preparations in order to minimize calculation overhead
    cfstonescount=0;
    for(i=0; i<121;i++) {
      cf[i]=field[i]^playturn;
      if ((cf[i] & fieldexoccupied)==fieldexoccupiedyes) {
        cfstonescount++;
        cf[i]&=fieldexoccpl;
      } else {
        cf[i]&=fieldexoccupied;
      }
    }
    if (cmvfrom==null) {
      cmvfrom=new int[cmvstack];
      cmvto=new int[cmvstack];
      cmvparent=new int[cmvstack];
      cmvpoints=new int[cmvstack];
      cmvdeepstart=new int[100];
    }
    cmvfrom[0]=magicfield;  // root entry in the tree
    cmvto[0]=magicfield;
    cmvparent[0]=0;
    cmvpoints[0]=0;
    cmvdeepstart[0]=1;
    cmvnw=1;
    cmvdragcount=dragcount;
    treed=0;
    cmvdeepness=0;
    ComputerLogGame();

    // Lay out all possibilities
    ComputerLogStatus("Checking possible moves ...");
    ComputerMoveMe(0);
    cmvdeepstart[1]=cmvnw;
    cmvcalculate=new int[cmvnw];

    // Can I win directly?
    if (cmvpoints[0]==cmvpwin) {
      ComputerLogStatus("Finished");
      computerfrom=cmvfrom[cmvnw-1];
      computerto=cmvto[cmvnw-1];
      computermessage="Thanks for the game.";
      ComputerLog(SusanGame.toString(computerfrom)+" "+SusanGame.toString(computerto)+" (id: "+i+") wins!");
      ComputerLogEnd();
      return true;
    }

    // Recursively calculate possibilities
    ComputerLogStatus("See what happens with interesting moves ...");
    ComputerMarkInterestingRoot();
    ComputerTreeCalc();
    ComputerLogTree();

    // Compare to database
    ComputerLogStatus("Comparing to database ...");
    ComputerLog("from 1 to "+cmvdeepstart[1]);
    ComputerTreeMove(0,0);
    ComputerDBCompare(1,cmvdeepstart[1]);

    // take one of the best moves
    // check if it is precalc
    while (true) {
      int smax=cmvpoints[1];
      int smaxc=1;
      int smaxci=0;
      if (cmvcalculate[1]==2) {smaxci++;}
      for (i=2; i<cmvdeepstart[1]; i++) {
        if (cmvpoints[i]==smax) {
          smaxc++;
          if (cmvcalculate[i]==2) {smaxci++;}
        } else if (cmvpoints[i]>smax) {
          smax=cmvpoints[i];
          smaxc=1;
          smaxci=0;
          if (cmvcalculate[i]==2) {smaxci++;}
        }
      }

      if (smaxci==0) {
        ComputerLogStatus("Calculate the database move ...");
        for (i=1; i<cmvdeepstart[1]; i++) {
          if (cmvpoints[i]==smax) {cmvcalculate[i]=1;}
        }
        ComputerTreeCalc();
        ComputerLogTree();
      } else {
        int take=(int)(Math.random()*smaxci);
        for (i=1; i<cmvdeepstart[1]; i++) {
          if ((cmvpoints[i]==smax) && (cmvcalculate[i]==2)) {
            if (take==0) {
              computerfrom=cmvfrom[i];
              computerto=cmvto[i];
              ComputerLogStatus("Finished");
              ComputerLogRoot();
              ComputerLog("");
              ComputerLog("Maximum points: "+smax);
              ComputerLog("Moves with max. points: "+smaxc);
              ComputerLog("Calculated moves with max. points: "+smaxci);
              if (smax>cmvpwinbound) {
                computermessage="This move will win.";
              } else if (smax<cmvploosebound) {
                computermessage="Congratulations, you win.";
              } else {
                computermessage="Points for this move: "+smax;
              }
              if (computerfrom==magicfield) {
                ComputerLog("Place "+SusanGame.toString(computerto)+" (id: "+i+")");
              } else {
                ComputerLog("Drag "+SusanGame.toString(computerfrom)+" "+SusanGame.toString(computerto)+" (id: "+i+")");
              }
              ComputerLogEnd();
              return true;
            }
            take--;
          }
        }
      }
    }
  }

  // Moves to a position in the tree, updating the computerfield
  private void ComputerTreeMove(int treei, int deepness) {
    int treefrom;
    int treeto;
    int wantdeepness=deepness;
    while (treed>deepness) {
      ComputerTreeMoveBack();
    }
    while (deepness>0) {
      if (treed==deepness) {
        if (tree[deepness]==treei) {break;}
        ComputerTreeMoveBack();
      }
      tree[deepness]=treei;
      treei=cmvparent[treei];
      deepness--;
    }
    while (treed<wantdeepness) {
      ComputerTreeMoveNext();
    }
  }

  private void ComputerTreeMoveBack() {
    int id=tree[treed];
    int treeto=cmvto[id];
    int treefrom=cmvfrom[id];
    if (treep==0) {
      treep=1;
      if (treefrom==magicfield) {
        cf[treeto]=fieldexists;
        cfstonescount--;
      } else {
        cf[treefrom]=cf[treeto];
        cf[treeto]=fieldexists;
      }
    } else {
      treep=0;
      if (treefrom==magicfield) {
        cf[treeto]=fieldexists;
        cfstonescount--;
      } else {
        cf[treefrom]=cf[treeto];
        cf[treeto]=fieldexists;
      }
    }
    treed--;
  }

  private void ComputerTreeMoveNext() {
    treed++;
    int id=tree[treed];
    int treeto=cmvto[id];
    int treefrom=cmvfrom[id];
    if (treep==0) {
      treep=1;
      if (treefrom==magicfield) {
        cf[treeto]=fieldexists | fieldoccupied;
        cfstonescount++;
      } else {
        cf[treeto]=cf[treefrom];
        cf[treefrom]=fieldexists;
      }
    } else {
      treep=0;
      if (treefrom==magicfield) {
        cf[treeto]=fieldexists | fieldoccupied | fieldplayer;
        cfstonescount++;
      } else {
        cf[treeto]=cf[treefrom];
        cf[treefrom]=fieldexists;
      }
    }
  }

  // Calculate my possibilities
  private void ComputerMoveMe(int id) {
    cmvcanwin=false;
    int i;
    int n;
    int t;
    if (cmvdragcount<5) {
      for (i=0; i<121; i++) {
        if ((cf[i] & fieldexoccpl)==fieldexoccupiedyes) {
          for (n=0; n<6; n++) {
            t=i+around[n];
            if ((cf[t] & fieldexoccupied)==fieldexoccupiedno) {
              cf[t]=cf[i];
              cf[i]=fieldexists;
              ComputerMoveMeCheck(id, i, t);
              cf[i]=cf[t];
              cf[t]=fieldexists;
              if (cmvcanwin) {
                cmvpoints[id]=cmvpwin;
                return;
              }
            }
          }
        }
      }
    }

    // Place a stone
    for (i=0; i<121; i++) {
      if ((cf[i] & fieldexoccupied)==fieldexoccupiedno) {
        cf[i]=fieldexists | fieldoccupied;
        ComputerMoveMeCheck(id, magicfield, i);
        cf[i]=fieldexists;
        if (cmvcanwin) {
          cmvpoints[id]=cmvpwin;
          return;
        }
      }
    }
  }

  // Check this move
  private void ComputerMoveMeCheck(int id, int lastfrom, int lastto) {
    int i=lastto;
    int a=0;
    int n=0;
    int ret=0;
    while (true) {
      if ((cf[i] & fieldexoccpl)==fieldexoccupiedyes) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {return;}
      } else if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {ret=1;}
      }
      if (a==6) {break;}
      i=lastto+around[a];
      a++;
    }
    if (ret==0) { //neighter winning nor loosing
      cmvfrom[cmvnw]=lastfrom;
      cmvto[cmvnw]=lastto;
      cmvparent[cmvnw]=id;
      cmvpoints[cmvnw]=ComputerMoveOther(10);
      cmvnw++;
    } else { //winning
      cmvfrom[cmvnw]=lastfrom;
      cmvto[cmvnw]=lastto;
      cmvparent[cmvnw]=id;
      cmvpoints[cmvnw]=cmvpwin;
      cmvnw++;
      cmvcanwin=true;
    }
  }

  // What could my adversaire do in the current situation?
  // This function returns an amount of points for the previous move.
  // If the opponent can win, return cmvploose.
  private int ComputerMoveOther(int pt) {
    int i;
    int n;
    int t;
    if (cmvdragcount<5) {
      for (i=0; i<121; i++) {
        if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
          for (n=0; n<6; n++) {
            t=i+around[n];
            if ((cf[t] & fieldexoccupied)==fieldexoccupiedno) {
              cf[t]=cf[i];
              cf[i]=fieldexists;
              pt=ComputerMoveOtherCheck(i, t, pt);
              cf[i]=cf[t];
              cf[t]=fieldexists;
              if (pt==cmvploose) {return cmvploose;}
            }
          }
        }
      }
    }

    // Place a stone
    for (i=0; i<121; i++) {
      if ((cf[i] & fieldexoccupied)==fieldexoccupiedno) {
        cf[i]=fieldexists|fieldoccupied|fieldplayer;
        pt=ComputerMoveOtherCheck(magicfield, i, pt);
        cf[i]=fieldexists;
        if (pt==cmvploose) {return cmvploose;}
      }
    }

    return pt;
  }

  // Check this move
  private int ComputerMoveOtherCheck(int lastfrom, int lastto, int pt) {
    int i=lastto;
    int a=0;
    int n=0;
    int ret=0;
    while (true) {
      if ((cf[i] & fieldexoccpl)==fieldexoccupiedyes) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {ret=1;}
      } else if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {return pt;}  // direct suicide by the other, we assume he doesn't make such a move
      }
      if (a==6) {break;}
      i=lastto+around[a];
      a++;
    }

    if (ret==0) { // neighter winning nor loosing
                  // here we could add some more checks and modify the points
      return pt;
    } else { //winning for the opponent
      return cmvploose;
    }
  }

  // Compares the current situation with the database
  private void ComputerDBCompare(int memfrom, int memto) {
    int r,n,j,i;
    SusanDBEntry sdbe;
    ComputerLog("Number of stones: "+cfstonescount);

    for (Iterator it = sdb.db.iterator(); it.hasNext(); ) {
      sdbe=(SusanDBEntry)it.next();
      if ((cfstonescount>=sdbe.minstones) || (sdbe.minstones==0)) {
      if ((cfstonescount<=sdbe.maxstones) || (sdbe.maxstones==0)) {
        for (r=0; r<12; r++) {
          n=r*121;
          for (i=0; i<121; i++) {
            if ((sdbe.field[i] & fielddontcare)==0) {
              if (sdbe.field[i]!=cf[SusanDBEntry.rot[n]]) {break;}
            }
            n++;
          }
          if (i==121) {
            ComputerLog("Recognized: "+sdbe.nam);
            for(n=0; n<sdbe.movefrom.length; n++) {
              int mfrom=sdbe.getmovefrom(n,r);
              int mto=sdbe.getmoveto(n,r);
              for (j=memfrom; j<memto; j++) {
                if ((cmvfrom[j]==mfrom) && (cmvto[j]==mto)) {ComputerLog("GIVE "+sdbe.movepoints[n]+" to "+toString(mfrom)+" "+toString(mto)); cmvpoints[j]+=sdbe.movepoints[n]; break;}
              }
            }
          }
        }
      }}
    }
  }

  public boolean ComputerCalcInterestingFields () {
    int fieldoc=fieldplayer | fieldoccupied;
    int fieldexoccpl0=fieldexoccupiedyes;
    int fieldexoccpl1=fieldexoccupiedyes | fieldplayer;
    int i,n,m,free;
    for (i=0; i<121; i++) {
      if ((cf[i] & fieldexoccupied)==fieldexoccupiedyes) {
        int plme=cf[i] & fieldplayer;
        int plother=plme^fieldplayer;
        cfmark[i] |= fieldtemp;
        for (n=0; n<6; n++) {
          cfmark[i+around[n]] |= fieldtemp;
        }
        for (n=0; n<6; n++) {
          m=i+around[n];
          if (((cf[m] & fieldexoccupied)==fieldexoccupiedno) || ((cf[m] & fieldexoccpl)==(fieldexoccupiedyes|plother))) {
            int w=0;
            for (; w<6; w++) {
              if ((cfmark[m+around[w]] & fieldtemp)!=0) {
              } else if ((cf[m+around[w]] & fieldexists)==0) {
              } else if ((cf[m+around[w]] & fieldexoccpl)==(fieldexoccupiedyes|plme)) {
              } else {
                break;
              }
            }
            if (w==6) {break;}
          }
        }
        cfmark[i]=0;
        if (n!=6) {cfmark[i]|=fieldmarkfort;}
        for (n=0; n<6; n++) {
          cfmark[i+around[n]] ^= fieldtemp;
        }
      } else {
        cfmark[i]=0;
      }
    }
    for (i=0; i<121; i++) {
      if (((cf[i] & fieldexoccupied)==fieldexoccupiedyes) && ((cfmark[i] & fieldmarkfort)==0)) {
        free=0;
        for (n=0; n<6; n++) {
          m=i+around[n];
          if ((cf[m] & fieldexoccupied)==fieldexoccupiedno) {
            free++;
            if (free==4) {break;}
          }
        }
        if (free<4) {
          cfmark[i]|=fieldmarkcalc;
          for (n=0; n<6; n++) {
            m=i+around[n];
            cfmark[m]|=fieldmarkcalc;
            if ((cf[m] & fieldexoccupied)==fieldexoccupiedno) {}
          }
        }
      }
    }
    return true;
  }

  private boolean ComputerWorthPrecalc(int from, int to) {
    if ((cfmark[to]&fieldmarkcalc)==0) {
      return ((cfmark[from]&fieldmarkcalc)!=0);
    }
    return true;
  }


  private void ComputerMarkInterestingRoot() {
    ComputerTreeMove(0,0);
    ComputerCalcInterestingFields();
    for (int i=1; i<cmvdeepstart[1]; i++) {
      if ((cmvpoints[i]>cmvploosebound) && (cmvpoints[i]<cmvpwinbound)) {
        if (ComputerWorthPrecalc(cmvfrom[i], cmvto[i])) {cmvcalculate[i]=1;}
      }
    }
  }

  private void ComputerTreeCalc() {
    cmvnw=cmvdeepstart[1];
    boolean cont=ComputerTreeCalcFirst();
    while (cont) {
      ComputerLog("");
      ComputerLog("Calculating "+(cmvdeepness+1)+". move in advance ...");
      if (! ComputerTreeCalcMe()) {ComputerTreeTrack(); break;}
      if (ComputerTreeTrack()) {break;}
      ComputerLog("");
      ComputerLog("Calculating "+(cmvdeepness+1)+". move in advance ...");
      if (! ComputerTreeCalcOther()) {ComputerTreeTrack(); break;}
      if (ComputerTreeTrack()) {break;}
    }
  }

  private boolean ComputerTreeCalcFirst() {
    int n,i;
    int oldcmvnw=cmvnw;

    cmvdeepness=1;
    for (i=1; i<oldcmvnw; i++) {
      if (cmvcalculate[i]==1) {
        if (cmvnw+cmvminimalstack>cmvstack) {return false;}
        cmvcalculate[i]=2;
        ComputerTreeMove(i,1);
        ComputerCalcInterestingFields();
        ComputerTreeOtherPossibility(i);
      }
    }

    return (oldcmvnw!=cmvnw);
  }

  private boolean ComputerTreeCalcMe() {
    int n;
    int i=cmvdeepstart[cmvdeepness];

    int oldcmvnw=cmvnw;
    cmvdeepness++;
    cmvdeepstart[cmvdeepness]=cmvnw;
    int firstchild=0;

    int oldparent=-1;
    while (i<oldcmvnw) {
      if (cmvnw+cmvminimalstack>cmvstack) {return false;}
      if (oldparent!=cmvparent[i]) {
        ComputerTreeMove(cmvparent[i],cmvdeepness-1);
        oldparent=cmvparent[i];
      }

      ComputerTreeMove(i,cmvdeepness);
      ComputerCalcInterestingFields();
      firstchild=cmvnw;
      if (ComputerTreeMePossibility(i)) {
        cmvpoints[i]=cmvpwin;
        cmvnw=firstchild;
      }
      i++;
    }
    return (oldcmvnw!=cmvnw);
  }

  private boolean ComputerTreeCalcOther() {
    int n;
    int i=cmvdeepstart[cmvdeepness];

    int oldcmvnw=cmvnw;
    cmvdeepness++;
    cmvdeepstart[cmvdeepness]=cmvnw;
    int firstchild=0;

    int oldparent=-1;
    while (i<oldcmvnw) {
      if (cmvnw+cmvminimalstack>cmvstack) {return false;}
      if (oldparent!=cmvparent[i]) {
        ComputerTreeMove(cmvparent[i],cmvdeepness-1);
        oldparent=cmvparent[i];
      }

      ComputerTreeMove(i,cmvdeepness);
      ComputerCalcInterestingFields();
      firstchild=cmvnw;
      if (ComputerTreeOtherPossibility(i)) {
        cmvpoints[i]=cmvploose;
        cmvnw=firstchild;
      }
      i++;
    }
    return (oldcmvnw!=cmvnw);
  }

  private boolean ComputerTreeTrack() {
    int p=0;
    int n=cmvnw;
    int id=cmvdeepness;
    if ((id % 2)==1) {
      ComputerTreeTrackOther(cmvdeepstart[id], n);
      n=cmvdeepstart[id];
      id--;
    }
    while (id>0) {
      ComputerTreeTrackMe(cmvdeepstart[id], n);
      n=cmvdeepstart[id];
      id--;
      ComputerTreeTrackOther(cmvdeepstart[id], n);
      n=cmvdeepstart[id];
      id--;
    }

    for (int i=1; i<cmvdeepstart[1]; i++) {
      if (cmvpoints[i]>cmvpwinbound) {return true;}
    }

    for (int i=cmvdeepstart[1]; i<cmvnw; i++) {
      p=cmvpoints[cmvparent[i]];
      if ((p>cmvpwinbound) || (p<cmvploosebound)) {cmvpoints[i]=p;}
    }
    return false;
  }

  private void ComputerTreeTrackMe(int first, int i) {
    int oldparent;
    boolean loosing;
    i--;
    while (i>=first) {
      oldparent=cmvparent[i];
      if ((cmvpoints[oldparent]<=cmvpwinbound) && (cmvpoints[oldparent]>=cmvploosebound)) {
        loosing=true;
        while (oldparent==cmvparent[i]) {
          if (cmvpoints[i]>0) {cmvpoints[oldparent]+=cmvpoints[i]; ComputerLog("Tracking winner "+i+" to "+oldparent); loosing=false; break;}
          if (cmvpoints[i]==0) {loosing=false;}
          i--;
        }
        if (loosing) {ComputerLog("Tracking looser to "+oldparent); cmvpoints[oldparent]+=cmvpoints[i+1];}
      }
      while (oldparent==cmvparent[i]) {i--;}
    }
  }

  private void ComputerTreeTrackOther(int first, int i) {
    int oldparent;
    boolean winning;
    i--;
    while (i>=first) {
      oldparent=cmvparent[i];
      if ((cmvpoints[oldparent]<=cmvpwinbound) && (cmvpoints[oldparent]>=cmvploosebound)) {
        winning=true;
        while (oldparent==cmvparent[i]) {
          if (cmvpoints[i]<0) {cmvpoints[oldparent]+=cmvpoints[i]; ComputerLog("Tracking looser "+i+" to "+oldparent); winning=false; break;}
          if (cmvpoints[i]==0) {winning=false;}
          i--;
        }
        if (winning) {ComputerLog("Tracking winner to "+oldparent); cmvpoints[oldparent]+=cmvpoints[i+1];}
      }
      while (oldparent==cmvparent[i]) {i--;}
    }
  }

  private boolean ComputerTreeOtherPossibility(int id) {
    int i;
    int n;
    int t;
    int pt;
    if (cmvdragcount<5) {
      for (i=0; i<121; i++) {
        if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
          for (n=0; n<6; n++) {
            t=i+around[n];
            if ((cf[t] & fieldexoccupied)==fieldexoccupiedno) {
              cf[t]=cf[i];
              cf[i]=fieldexists;
              pt=ComputerTreeOtherPossibilityCheck(id, i, t);
              cf[i]=cf[t];
              cf[t]=fieldexists;
              if (pt==cmvploose) {return true;}
            }
          }
        }
      }
    }

    // Place a stone
    for (i=0; i<121; i++) {
      if ((cf[i] & fieldexoccupied)==fieldexoccupiedno) {
        cf[i]=fieldexists|fieldoccupied|fieldplayer;
        pt=ComputerTreeOtherPossibilityCheck(id, magicfield, i);
        cf[i]=fieldexists;
        if (pt==cmvploose) {return true;}
      }
    }

    return false;
  }

  // Check this move
  private int ComputerTreeOtherPossibilityCheck(int id, int lastfrom, int lastto) {
    int i=lastto;
    int a=0;
    int n=0;
    int ret=0;
    while (true) {
      if ((cf[i] & fieldexoccpl)==fieldexoccupiedyes) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {ret=1;}
      } else if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {return 0;}  // direct suicide by the other, we assume he doesn't make such a move
      }
      if (a==6) {break;}
      i=lastto+around[a];
      a++;
    }

    if (ret==0) { // neighter winning nor loosing
                  // here we could add some more checks and modify the points
      if (ComputerWorthPrecalc(lastfrom,lastto)) {
        cmvfrom[cmvnw]=lastfrom;
        cmvto[cmvnw]=lastto;
        cmvparent[cmvnw]=id;
        cmvpoints[cmvnw]=0;
        cmvnw++;
        return 1;
      } else {
        return 0;
      }
    } else { //winning for the opponent
      return cmvploose;
    }
  }

  private boolean ComputerTreeMePossibility(int id) {
    int i;
    int n;
    int t;
    int pt;
    if (cmvdragcount<5) {
      for (i=0; i<121; i++) {
        if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes)) {
          for (n=0; n<6; n++) {
            t=i+around[n];
            if ((cf[t] & fieldexoccupied)==fieldexoccupiedno) {
              cf[t]=cf[i];
              cf[i]=fieldexists;
              pt=ComputerTreeMePossibilityCheck(id, i, t);
              cf[i]=cf[t];
              cf[t]=fieldexists;
              if (pt==cmvpwin) {return true;}
            }
          }
        }
      }
    }

    // Place a stone
    for (i=0; i<121; i++) {
      if ((cf[i] & fieldexoccupied)==fieldexoccupiedno) {
        cf[i]=fieldexists|fieldoccupied;
        pt=ComputerTreeMePossibilityCheck(id, magicfield, i);
        cf[i]=fieldexists;
        if (pt==cmvpwin) {return true;}
      }
    }

    return false;
  }

  // Check this move
  private int ComputerTreeMePossibilityCheck(int id, int lastfrom, int lastto) {
    int i=lastto;
    int a=0;
    int n=0;
    int ret=0;
    while (true) {
      if ((cf[i] & fieldexoccpl)==fieldexoccupiedyes) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {return 0;}
      } else if ((cf[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
        for (n=0; n<6; n++) {
          if ((cf[i+around[n]] & fieldexoccupied)==fieldexoccupiedno) {break;}
        }
        if (n==6) {ret=1;}
      }
      if (a==6) {break;}
      i=lastto+around[a];
      a++;
    }
    if (ret==0) { //neighter winning nor loosing
      if (ComputerWorthPrecalc(lastfrom,lastto)) {
        cmvfrom[cmvnw]=lastfrom;
        cmvto[cmvnw]=lastto;
        cmvparent[cmvnw]=id;
        cmvpoints[cmvnw]=0;
        cmvnw++;
        return 1;
      } else {
        return 0;
      }
    } else { //winning
      return cmvpwin;
    }
  }

  // Sets the stack size, the most important factor of calculation deepness
  public void setComputerStack(int stacksize) {
    if (stacksize < cmvminimalstack) {stacksize=cmvminimalstack;}
    cmvstack=stacksize;
  }

  // Returns the stack size
  public int getComputerStack() {
    return cmvstack;
  }

  // ######################### COMPUTER LOG METHODS

  // Starts the ComputerTrace
  private void ComputerLogStart(boolean writefile) {
    eventhandler.SusanGameComputerBegin();
    if (writefile) {
      try {
        clog = new PrintStream(new FileOutputStream("computercalc.txt"));
      } catch (IOException e) {
      }
    }
  }

  // Ends the ComputerTrace
  private void ComputerLogEnd() {
    if (clog!=null) {clog.close();}
    eventhandler.SusanGameComputerEnd();
  }

  // Adds a message to the log
  private void ComputerLog(String t) {
    if (clog!=null) {clog.println(t);}
  }

  // Changes the status
  private void ComputerLogStatus(String status) {
    if (clog!=null) {
      ComputerLog("");
      ComputerLog("----------------------------------------");
      ComputerLog(status);
      ComputerLog("----------------------------------------");
    }
    eventhandler.SusanGameComputerChangeStatus(status);
  }

  // Adds a calculated tree with a message to clog
  private void ComputerLogTrace(int i, String t) {
    if (clog==null) {return;}
    while (i>0) {
      if (cmvfrom[i]==magicfield) {
        t=toString(cmvto[i])+"   ="+cmvpoints[i]+" ("+i+") - "+t;
      } else {
        t=toString(cmvfrom[i])+">"+toString(cmvto[i])+"="+cmvpoints[i]+" ("+i+") - "+t;
      }
      i=cmvparent[i];
    }
    ComputerLog(t);
  }

  private void ComputerLogGame() {
    if (clog==null) {return;}
    printGame(clog, cf);
  }

  private void ComputerLogTree() {
    if (clog==null) {return;}
    ComputerLog("");
    ComputerLog("Stack reserved for calculation (items): "+cmvstack);
    ComputerLog("Stack reserved for one step (items): "+cmvminimalstack);
    ComputerLog("Stack used: "+cmvnw);
    ComputerLog("");
    ComputerLog("Calculation deepness: "+cmvdeepness);
    for (int i=0; i<=cmvdeepness; i++) {
      ComputerLog("  cmvdeepstart["+i+"]="+cmvdeepstart[i]);
    }
    ComputerLog("");
    ComputerLog("Current position in tree:");
    ComputerLog("  deepness: "+treed);
    for (int i=0; i<treed; i++) {
      ComputerLog("  tree["+i+"]: "+tree[i]);
    }
  }

  private void ComputerLogRoot() {
    ComputerLog("");
    ComputerLog("Root:");
    for (int i=1; i<cmvdeepstart[1]; i++) {
      ComputerLogTrace(i,"");
    }
  }

  // ######################### SAVE AND LOAD GAME
  // These methods are used to save a game or to reload it.

  // Saves the game at the current state in a binary file.
  // Only the history is saved because this is enough to reconstruct the game.
  public boolean saveGame(File filename) {
    boolean success=true;
    FileOutputStream st1 = null;
    DataOutputStream st2 = null;
    try {
      st1 = new FileOutputStream(filename);
      st2 = new DataOutputStream(st1);

      st2.writeInt(0x4C415741);
      for (Iterator it = history.iterator(); it.hasNext(); ) {
        SusanGameHistoryElement sghe=(SusanGameHistoryElement)it.next();
        st2.writeInt(sghe.from);
        st2.writeInt(sghe.to);
        st2.writeInt(sghe.olddragcount);
      }
    } catch (IOException e) {
      log("An error occured while writing to the file "+filename+". The game has not been saved.");
      success=false;
    } finally {
      try {
        if (st2 != null) {st2.close();}
        if (st1 != null) {st1.close();}
      } catch (IOException e) {
        log("An error occured while closing the file "+filename+". The game may not be saved correctly.");
        success=false;
      }
    }
    return success;
  }

  // Loads a game.
  // If the file cannot be read binary, try reading it as a text file.
  public boolean loadGame(File filename) {
    boolean success;
    int oldhistorypos=historypos;
    HistoryBack(oldhistorypos);

    if (loadGameBinary(filename)) {
      success=true;
    } else {
      success=loadGameText(filename);
    }

    if (success) {
      eventhandler.SusanGameHistoryChanged();
      HistoryNext(history.size());
    } else {
      HistoryNext(oldhistorypos);
    }
    return success;
  }

  // Opens a game file and reads it following the LaWa binary file format.
  // If it fails, return false and do not touch the current game.
  private boolean loadGameBinary(File filename) {
    boolean success=true;
    java.util.List readhistory=new ArrayList();
    DataInputStream st2 = null;
    try {
      st2 = new DataInputStream(new FileInputStream(filename));

      if (st2.readInt()!=0x4C415741) { // LAWA binary file
        success=false;
        new EOFException();
      }

      while (true) {
        int from = st2.readInt();
        int to = st2.readInt();
        int olddragcount = st2.readInt();
        readhistory.add(new SusanGameHistoryElement(from,to,olddragcount));
      }
    } catch (EOFException e) {
    } catch (IOException e) {
      log("An error occured while reading from the file "+filename+".");
      success=false;
    } finally {
      try {
        if (st2 != null) {st2.close();}
      } catch (IOException e) {
        log("An error occured while closing the file "+filename+".");
        success=false;
      }
    }
    if (success) {
      history=readhistory;
    }
    return success;
  }

  // Opens a text file and filters out the moves.
  // All unrecognized lines are sent to the log.
  // If it fails, return false and do not touch the current game.
  private boolean loadGameText(File filename) {
    boolean success=false;
    java.util.List readhistory=new ArrayList();
    int readdragcount=0;

    BufferedReader st2 = null;
    try {
      st2 = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
      int stat=0;

      String line=st2.readLine();
      while (line!=null) {
        int pos=line.indexOf(" MOVED ");
        if (pos>-1) {
          int pos1=line.indexOf(" ",pos+7);
          String sfrom;
          String sto;
          if (pos1>-1) {
            sfrom=line.substring(pos+7,pos1);
            sto=line.substring(pos1+1);
          } else {
            sfrom="";
            sto=line.substring(pos+7);
          }
          int from=getPosition(sfrom);
          int to=getPosition(sto);
          if (to==magicfield) {
            log("invalid move: "+line);
          } else {
            success=true;
            readhistory.add(new SusanGameHistoryElement(from,to,readdragcount));
            if (from==magicfield) {
              readdragcount=0;
            } else {
              readdragcount++;
            }
          }
        } else {
          log(line);
        }
        line=st2.readLine();
      }
    } catch (EOFException e) {
    } catch (IOException e) {
      log("An error occured while reading from the file "+filename+".");
      success=false;
    } finally {
      try {
        if (st2 != null) {st2.close();}
      } catch (IOException e) {
        log("An error occured while closing the file "+filename+".");
        success=false;
      }
    }
    if (success) {
      history=readhistory;
    }
    return success;
  }

  // ######################### VARIOUS FUNCTIONS

  // Add a log entry
  public void log(String t) {
    //System.out.println(t); //for debugging
    eventhandler.SusanGameLogEntry(t);
  }

  // Get's the current status
  public int getStatus() {
    return status;
  }

  // Starts a game
  public void start() {
    eventhandler.SusanGamePrepareNextTurn(playturn);
    eventhandler.SusanGameHistoryChanged();
    status=statusplaying;
  }

  // Starts a game
  public void stop() {
    status=statusstopped;
  }

  // Prints the current game in a given PrintStream
  public void printGame(PrintStream out) {
    printGame(out,field);
  }

  // Prints the current game in System.out
  public void printGame() {
    printGame(System.out,field);
  }

  // Prints the game stored in fl in a PrintStream
  private void printGame(PrintStream out, int[] fl) {
    int y;
    for (y=1; y<6; y++) {
      printGameLine(out, fl, 5-y, (char)(y+96), y*11+1, y+4);
    }
    for (; y<10; y++) {
      printGameLine(out, fl, y-5, (char)(y+96), y*11+y-4, 14-y);
    }
  }

  // Prints one line of the game in a PrintStream
  private void printGameLine(PrintStream out, int[] field, int before, char cc, int i, int icount) {
      while (before>0) {
        before--;
        out.print(" ");
      }
      out.print(cc+" ");
      while (icount>0) {
        if ((field[i] & fieldexoccpl)==fieldexoccupiedyes) {
          out.print("o ");
        } else if ((field[i] & fieldexoccpl)==(fieldexoccupiedyes|fieldplayer)) {
          out.print("x ");
        } else {
          out.print(". ");
        }
        icount--;
        i++;
      }
      out.println("");
  }
}

// ######################### END OF FILE
